package org.game.manager;

import java.util.Calendar;
import java.util.Date;
import java.util.List;
import java.util.Map;

import org.apache.commons.collections4.MapUtils;
import org.apache.commons.lang3.tuple.ImmutablePair;
import org.apache.commons.lang3.tuple.Pair;
import org.cfg.SummonCfg;
import org.cfg.SummonComposeCfg;
import org.cfg.SummonPoolCfg;
import org.cfg.cache.DiscreteDataCfgCache;
import org.cfg.cache.SummonComposeCfgCache;
import org.cfg.cache.SummonPoolCfgCache;
import org.constant.DiscreteDataID;
import org.constant.DiscreteDataKey;
import org.constant.EventConditionKey;
import org.constant.EventConditionType;
import org.constant.EventType;
import org.constant.smallkey.PlayerDataSmallKey;
import org.game.constant.SummonConstant;
import org.game.db.bean.ComposeSummon;
import org.game.db.bean.Player;
import org.game.db.bean.PlayerData;
import org.game.db.vo.PlayerDataVO.DiscreteVO;
import org.game.log.builder.LogDsp;
import org.game.log.dsp.GoodsDsp;
import org.game.msgBuilder.SummonMsgBuilder;
import org.game.plugin.event.GameEvent;
import org.game.plugin.event.GameEventPlugin;
import org.game.protobuf.s2c.S2CItemMsg;
import org.game.util.ProtoUnit;
import org.utils.CommonUtil;

import com.google.common.collect.Lists;
import com.google.common.collect.Maps;
import com.xiaoleilu.hutool.date.DateUtil;
import com.xiaoleilu.hutool.util.RandomUtil;

/**
 * 召唤管理器
 * Created by fxf on 2017-12-23.
 */
public class SummonManager {

    private SummonManager() {
    }

    public static byte[] summon(Player player, SummonCfg summonCfg, Map realCost) {

        ItemManager.costGoodsByCid(player,realCost, LogDsp.newBuilder(GoodsDsp.SUMMON).targetTag(summonCfg.getId()), S2CItemMsg.ItemList.newBuilder());

        int weightType = getWeightType(player, summonCfg);
        List<Pair> resultList = Lists.newArrayList();
        Map<Integer, Integer> obtainTypeNum = Maps.newHashMap();
        if (summonCfg.getCardCount() == 1) {
            //获取普通道具
            getGeneralItem(player, summonCfg, weightType, resultList,obtainTypeNum);
        } else {
            //获取稀有道具
            getRareItem(player, summonCfg, weightType, resultList,obtainTypeNum);
            //获取普通道具
            getGeneralItem(player, summonCfg, weightType, resultList,obtainTypeNum);
        }

        ItemManager.addGoodsAndPush(player,null,null,summonCfg.getGoods(), LogDsp.newBuilder(GoodsDsp.SUMMON));
        ItemManager.addGoodsAndPush(player,null,null,packageMap(resultList), LogDsp.newBuilder(GoodsDsp.SUMMON));
        
        
        Map<String, Object> in = Maps.newHashMap();
		in.put(EventConditionKey.CONDITION_TYPE, EventConditionType.SUMMON);
		in.put(EventConditionKey.CARD_COUNT, summonCfg.getCardCount());
		GameEvent event = new GameEvent(player, EventType.OTHER_EVENT, in);
		GameEventPlugin.syncSubmit(event);
		
        return ProtoUnit.toByte(SummonMsgBuilder.buildSummonMsg(resultList));
    }

    public static byte[] composeSummon(Player player, SummonComposeCfg summonComposeCfg){

        ItemManager.costGoodsByCid(player,summonComposeCfg.getCost(),LogDsp.newBuilder(GoodsDsp.COMPOSE_SUMMON).addAttr("cid", summonComposeCfg.getId()), S2CItemMsg.ItemList.newBuilder());

        Calendar finishTime = Calendar.getInstance();
        finishTime.add(Calendar.SECOND,summonComposeCfg.getTime());

        ComposeSummon composeSummon = new ComposeSummon(0,player.getId(),summonComposeCfg.getZPointType(),summonComposeCfg.getId(),finishTime.getTime());
        composeSummon.insert();
        player.getPlayerProxy().startSecondsJob();

        Map<String, Object> in = Maps.newHashMap();
		in.put(EventConditionKey.CONDITION_TYPE, EventConditionType.COMPOUND);
		GameEvent event = new GameEvent(player, EventType.OTHER_EVENT, in);
		GameEventPlugin.syncSubmit(event);
        
        return ProtoUnit.toByte(SummonMsgBuilder.buildComposeSummonMsg(composeSummon));
    }

    public static byte[] composeFinish(Player player, ComposeSummon composeSummon){
        SummonComposeCfg cfg = SummonComposeCfgCache.me().getById(composeSummon.getCid());
        Map<Integer,Integer> itemMap = getComposeItem(cfg.getPoolType());
        ItemManager.addGoodsAndPush(player,null,null,itemMap,LogDsp.newBuilder(GoodsDsp.COMPOSE_SUMMON).targetTag(cfg.getId()));
        composeSummon.delete();
        return ProtoUnit.toByte(SummonMsgBuilder.buildComposeFinishMsg(itemMap,composeSummon.getZPointType()));
    }


    //↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓ private method ↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓

    /**
     * 获取权重类型
     */
    private static int getWeightType(Player player, SummonCfg summonCfg) {
        //1.判断是否新手期
        int newPlayerTime = DiscreteDataCfgCache.me().getIntData(DiscreteDataID.SUMMON, DiscreteDataKey.NEW_PLAYER_TIME);
        long day = DateUtil.betweenDay(player.getRegisterDate(), new Date(), false);
        if (day > newPlayerTime) {
            //新手期
            if (summonCfg.getCardCount() == 1) {
                return SummonConstant.WEIGHT_INDEX_OLD_PLAYER_SINGLE;
            } else {
                return SummonConstant.WEIGHT_INDEX_OLD_PLAYER_TEN_TIMES;
            }
        } else {
            //新手期
            if (summonCfg.getCardCount() == 1) {
                return SummonConstant.WEIGHT_INDEX_NEW_PLAYER_SINGLE;
            } else {
                return SummonConstant.WEIGHT_INDEX_NEW_PLAYER_TEN_TIMES;
            }

        }
    }

    private static int getRareItemNum(SummonCfg summonCfg) {
        if (summonCfg.getRareItemNum() == null || summonCfg.getRareItemNum().size() == 0){
            return 0;
        }
        Map<Integer, Integer> map = Maps.newHashMap();
        int weight = 0;
        for (Object o : summonCfg.getRareItemNum().entrySet()) {
            Map.Entry<Integer, Integer> entry = (Map.Entry<Integer, Integer>) o;
            weight += entry.getValue();
            map.put(weight, entry.getKey());
        }

        int random = RandomUtil.randomInt(SummonConstant.RARE_ITEM_RADOM_LIMIT);
        for (Integer key : map.keySet()) {
            if (random <= key)
                return map.get(key);
        }
        return 0;
    }

    /**
     * 获取稀有道具
     */
    public static void getRareItem(Player player, SummonCfg summonCfg, int weightType, List<Pair> out,Map<Integer, Integer> obtainTypeNum) {
        int rareNum = getRareItemNum(summonCfg);
        if (rareNum <= 0){
            return;
        }
        List<SummonPoolCfg> pool = SummonPoolCfgCache.me().getByPoolType(summonCfg.getPoolType());
        List<SummonPoolCfg> firstSelectPool = Lists.newArrayList();
        PlayerData pd = player.getPlayerDatasFkPlayerId();
        DiscreteVO discreteVO = pd.getPlayerDataProxy().getDiscreteVO();
        int rankScore = discreteVO.getSummonRankScore();  
        int maxWeight = 0;

        //初选（根据品质筛选）及 计算最大权重
        for (SummonPoolCfg poolCfg : pool) {
            if (poolCfg.getQuality() < summonCfg.getMinQuality()[0] || poolCfg.getQuality() > summonCfg.getMinQuality()[1])
                continue;
            if (!checkLimitTypeCount(summonCfg, poolCfg, obtainTypeNum)) {
            	continue;
			}
            firstSelectPool.add(poolCfg);
            maxWeight += poolCfg.getWeight()[weightType];
        }

        //复选（根据权重筛选）
        for (int i = 0; i < rareNum; i++) {
            int randomWeight = RandomUtil.randomInt(maxWeight);
            int totalWeight = 0;
            for (SummonPoolCfg poolCfg : firstSelectPool) {
                totalWeight += poolCfg.getWeight()[weightType];
                if (totalWeight >= randomWeight) {
//                	obtainTypeNum.
                	CommonUtil.changeMap(obtainTypeNum, poolCfg.getType(), 1);
                    poolCfg.getItemMap().forEach((k,v)-> out.add(ImmutablePair.of(k,v)));
                    rankScore += poolCfg.getScore();
                    break;
                }
            }
        }
        rankScore = fixRankScore(rankScore);
        discreteVO.setSummonRankScore(rankScore);
        pd.update();
    }

    /**
     * 修正rankScore
     */
    private static int fixRankScore(int rankScore) {
        rankScore = rankScore > SummonConstant.RANK_SCORE_UPPER_LIMIT ? SummonConstant.RANK_SCORE_UPPER_LIMIT : rankScore;
        rankScore = rankScore < SummonConstant.RANK_SCORE_LOWER_LIMIT ? SummonConstant.RANK_SCORE_LOWER_LIMIT : rankScore;
        return rankScore;
    }

    /**
     * 限制道具类型
     * @return
     */
    private static boolean checkLimitTypeCount(SummonCfg summonCfg,SummonPoolCfg poolCfg,Map<Integer, Integer> obtainTypeNum){
    	boolean bool = true;
        int itemType = poolCfg.getType();
        if (summonCfg.getItemTypeNum().containsKey(itemType)) {
			int limitCount = MapUtils.getIntValue(summonCfg.getItemTypeNum(), itemType);
			
			int obtainCount = MapUtils.getIntValue(obtainTypeNum, itemType);
			if (obtainCount >= limitCount) {
				bool = false;
			}
		}
        
        return bool;
    }
    
    /**
     * 获取普通道具
     */
    public static void getGeneralItem(Player player, SummonCfg summonCfg, int weightType, List<Pair> out,Map<Integer, Integer> obtainTypeNum) {
        List<SummonPoolCfg> pool = SummonPoolCfgCache.me().getByPoolType(summonCfg.getPoolType());
        PlayerData pd = player.getPlayerDatasFkPlayerId();
        DiscreteVO discreteVO = pd.getPlayerDataProxy().getDiscreteVO();
        int rankScore = discreteVO.getSummonRankScore();   
        int count = summonCfg.getCardCount() - out.size();

        for (int i = 0; i < count; i++) {
            List<SummonPoolCfg> firstSelectPool = Lists.newArrayList();
            int maxWeight = 0;
            for (SummonPoolCfg poolCfg : pool) {
                if (count !=1 && poolCfg.getQuality() >= summonCfg.getMinQuality()[0] && poolCfg.getQuality() <= summonCfg.getMinQuality()[1]){
                    continue;
                }
                if (!checkLimitTypeCount(summonCfg, poolCfg, obtainTypeNum)) {
                	continue;
				}
                if (poolCfg.getScoreLimit() >= rankScore) {
                    firstSelectPool.add(poolCfg);
                    maxWeight += poolCfg.getWeight()[weightType];
                }
            }
            int randomWeight = RandomUtil.randomInt(maxWeight);
            int totalWeight = 0;
            for (SummonPoolCfg poolCfg : firstSelectPool) {
                totalWeight += poolCfg.getWeight()[weightType];
                if (totalWeight >= randomWeight) {
                	CommonUtil.changeMap(obtainTypeNum, poolCfg.getType(), 1);
                    poolCfg.getItemMap().forEach((k,v)->{out.add(ImmutablePair.of(k,v));});
                    rankScore += poolCfg.getScore();
                    rankScore = fixRankScore(rankScore);
                    break;
                }
            }
        }

        discreteVO.setSummonRankScore(rankScore);
        pd.update();
    }


    public static Map<Integer,Integer> getComposeItem(int poolType){
        List<SummonPoolCfg> pool = SummonPoolCfgCache.me().getByPoolType(poolType);
        int maxWeight = 0;
        for (SummonPoolCfg poolCfg : pool) {
            maxWeight += poolCfg.getWeight()[SummonConstant.WEIGHT_INDEX_OLD_PLAYER_SINGLE];
        }
        int randomWeight = RandomUtil.randomInt(maxWeight);
        int totalWeight = 0;
        for (SummonPoolCfg poolCfg : pool) {
            totalWeight += poolCfg.getWeight()[SummonConstant.WEIGHT_INDEX_OLD_PLAYER_SINGLE];
            if (totalWeight >= randomWeight){
                return poolCfg.getItemMap();
            }
        }
        return null;
    }

    private static Map<Integer,Integer> packageMap(List<Pair> pairList){
        Map<Integer,Integer> result = Maps.newHashMap();
        pairList.forEach(pair -> {
            int num = result.computeIfAbsent((Integer) pair.getLeft(), k->0);
            result.put((Integer) pair.getLeft(),num + (int) pair.getRight());
        });
        return result;
    }



}
